package com.dylan.channel.v11;

import com.dylan.channel.v11.attribute.AttributeKey;
import com.dylan.channel.v11.channel.Channel;
import com.dylan.channel.v11.channel.NioSocketChannel;
import com.dylan.channel.v11.future.ChannelFuture;
import com.dylan.channel.v11.future.ChannelPromise;
import com.dylan.channel.v11.future.DefaultChannelPromise;
import com.dylan.channel.v11.option.ChannelOption;

import java.net.SocketAddress;
import java.util.Map;

import static com.dylan.channel.v11.ServerBootstrap.setChannelOptions;


public class BootStrap extends AbstractBootStrap<BootStrap, NioSocketChannel> {


    public BootStrap group(EventLoopGroup group) {
        this.group = group;
        return this;
    }

    public ChannelFuture connect(SocketAddress remoteAddress) {
        ChannelFuture future = initAndRegister();
        return doResolveAndConnect(remoteAddress, null);
    }
    @Override
    @SuppressWarnings("unchecked")
    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options);
        }
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Map.Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }
    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        //这里的逻辑和serverbootstarp一样，但是在这里又要写一遍该方法，现在是不是发现，如果bootstarp和serverbootstarp有一个
        //抽象父类就好了，就可以在父类中定义模版方法了。实际上源码中确实有一个父类，这个方法被定义在父类中，但我们暂时还不引入
        //整个方法和serverbootstrap中的doBind方法类似，判断和处理逻辑几乎一样
        final ChannelFuture regFuture = initAndRegister();
        //得到要注册的kehuduanchannel
        final Channel channel = regFuture.channel();
        //完成的情况下，直接开始执行绑定端口号的操作,首先创建一个future
        ChannelPromise promise = new DefaultChannelPromise(channel);
        return doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
    }


    private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                               final SocketAddress localAddress, final ChannelPromise promise) {
        //···
        //前面有一大段解析器解析远程地址的逻辑，在这里我删除了，那些不是重点，我们先关注重点
        doConnect(remoteAddress, localAddress, promise);
        return promise;
    }


    private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
        //得到客户端的channel
        final Channel channel = connectPromise.channel();
        //仍然是异步注册
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    //这里会走这个，我们并没有传递localAddress的地址
                    channel.connect(remoteAddress, null, connectPromise);
                }
            }
        });
    }
}
